/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.ponly.uno4j.core;

import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.container.XNameAccess;
import com.sun.star.container.XNameContainer;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XController;
import com.sun.star.frame.XModel;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.XComponent;
import com.sun.star.lib.uno.helper.PropertySet;
import com.sun.star.sheet.XSpreadsheetDocument;
import com.sun.star.sheet.XSpreadsheetView;
import com.sun.star.sheet.XSpreadsheets;
import com.sun.star.style.PageStyleLayout;
import com.sun.star.style.XStyle;
import com.sun.star.style.XStyleFamiliesSupplier;
import com.sun.star.uno.UnoRuntime;
import org.ponly.common.util.IOUtils;
import org.ponly.uno4j.io.SeekableInputStream;
import org.ponly.uno4j.util.UnoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;

/**
 * UNO 转换支持类
 *
 * @author vacoor
 * @see <a href="https://wiki.openoffice.org/wiki/API/Tutorials/PDF_export">OOo PDF Export</a>
 * @see <a href="http://www.openoffice.org/api/docs/common/ref/com/sun/star/document/MediaDescriptor.html">OOo MediaDescriptor</a>
 */
public abstract class UnoConv extends UnoSupport {
    private static final Logger LOG = LoggerFactory.getLogger(UnoConv.class);

    protected final String extension;

    protected UnoConv(String extension) {
        this.extension = extension;
    }

    /**
     * 使用给定 UNO Bridge 加载给定文件并转存到输出流
     *
     * @param unoBridge       UNO-Bridge
     * @param source          要加载的文件
     * @param loadProperties  加载属性
     * @param out             输出流
     * @param storeProperties 存储选项
     * @throws Exception
     */
    protected void convert(UnoBridge unoBridge, File source, PropertyValue[] loadProperties, OutputStream out, PropertyValue[] storeProperties) throws Exception {
        if (!unoBridge.isConnected()) {
            unoBridge.connect();
        }

        XComponentLoader loader = unoBridge.createComponentLoader();

        LOG.info("加载 {}", source);

        // TODO 如果是文本文件可能无法获取, 需要有扩展名
        XComponent doc = load(loader, source, loadProperties);

        /*
        XStyleFamiliesSupplier xSupplier = (XStyleFamiliesSupplier) UnoRuntime.queryInterface(XStyleFamiliesSupplier.class, doc);
        // Use the StyleFamiliesSupplier interface to get the XNameAccess interface of the
        // actual style families
        XNameAccess xFamilies = (XNameAccess) UnoRuntime.queryInterface(XNameAccess.class, xSupplier.getStyleFamilies());
        // Access the 'PageStyles' Family
        XNameContainer  xFamily = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, xFamilies.getByName("PageStyles"));
        XSpreadsheetDocument spreadsheetDocument = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class, doc);
        XModel model = (XModel) UnoRuntime.queryInterface(XModel.class, spreadsheetDocument);
        XController controller = model.getCurrentController();
        XSpreadsheetView view = (XSpreadsheetView) UnoRuntime.queryInterface(XSpreadsheetView.class,
                controller);
        XPropertySet xps = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, view.getActiveSheet());
        String pageStyleName = xps.getPropertyValue("PageStyle") + "";
        XStyle xStyle = (XStyle) UnoRuntime.queryInterface(XStyle.class, xFamily.getByName(pageStyleName));
        XPropertySet xStyleProps = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, xStyle);
        xStyleProps.setPropertyValue("ScaleToPages", new Short((short) 1));
        */

        /*
        XStyleFamiliesSupplier xSupplier = UnoUtils.$q(XStyleFamiliesSupplier.class, doc);
        XNameAccess xFamilies = UnoUtils.$q(XNameAccess.class, xSupplier.getStyleFamilies());
        XNameContainer xFamily;
        try {
            xFamily = UnoUtils.$q(XNameContainer.class, xFamilies.getByName("PageStyles"));
            XStyle xStyle = UnoUtils.$q(XStyle.class, xFamily.getByName("Default"));

            XPropertySet xStyleProps = UnoUtils.$q(XPropertySet.class, xStyle);
            xStyleProps.setPropertyValue("ScaleToPages", new Short((short) 1));
            xStyleProps.setPropertyValue("ScaleToPagesX", new Short((short) 1));
            xStyleProps.setPropertyValue("ScaleToPagesY", new Short((short) 1));

        } catch (Throwable ex) {

        }
        */


        /*
        XSpreadsheetDocument sheetDoc = UnoUtils.$q(XSpreadsheetDocument.class, doc);
        XSpreadsheets sheets = sheetDoc.getSheets();
        */

//        Property[] properties = ps.getPropertySetInfo().getProperties();
        String filterName = OOoRegistry.getFilterName(doc, extension);

        if (null == filterName) {
            throw new UnsupportedOperationException("无法识别输出文件格式, " + " extension: " + extension);
        }

        LOG.info("开始转换 {}, 使用filter: {}", source, filterName);

        XStorable xStorable = UnoUtils.$q(XStorable.class, doc);
        store(xStorable, out, UnoUtils.merge(storeProperties, UnoUtils.of("FilterName", filterName)));

        UnoUtils.disposeIf(doc);
        UnoUtils.closeIf(doc);

        LOG.info("转换成功: {} --> {}", source, out);
    }

    /**
     *
     */
    protected void convert(UnoBridge unoBridge, InputStream source, PropertyValue[] loadProperties,
                           OutputStream destination, PropertyValue[] storeProperties) throws Exception {
        File tempFile = File.createTempFile(getClass().getSimpleName(), ".tmp");
        IOUtils.flow(Channels.newChannel(source), new FileOutputStream(tempFile).getChannel(), true, true);

        convert(unoBridge, tempFile, loadProperties, destination, storeProperties);

        tempFile.delete();
    }

    /**
     * 发现使用 Java InputStream 方式效率要低很多, 该方法已经改为调用
     * {@link #convert(UnoBridge, File, PropertyValue[], OutputStream, PropertyValue[])}
     *
     * @deprecated
     */
    protected void convert(UnoBridge unoBridge, SeekableInputStream in, PropertyValue[] loadProperties
            , OutputStream out, PropertyValue[] storeProperties) throws Exception {
        convert(unoBridge, (InputStream) in, loadProperties, out, storeProperties);
    }
}
